#include "miscsampledev.h"

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/slab.h> // 内存操作
#include <linux/sched/signal.h> // 信号
#include <linux/uaccess.h> // 内核空间和用户空间数据传输
#include <linux/random.h>

#define MAX_UUID_LEN 20

enum misc_sample_retcode {
	MISC_SAMPLE_RET_OK = 0,
	MISC_SAMPLE_RET_FAIL = 1,
	MISC_SAMPLE_RET_NOMEM = 2,
	MISC_SAMPLE_RET_INTR = 3,
	MISC_SAMPLE_RET_WAIT = 4,
	MISC_SAMPLE_RET_UNKNOWN = 5,
};

typedef struct misc_sample_request_t {
	pid_t pid;
	int result;
	struct list_head entry;
	char message[0];
} misc_sample_request;

static struct misc_sample_dev_t {
	struct miscdevice misc;
	pid_t controller;
	struct mutex lock;
	struct list_head head;
	wait_queue_head_t response_wq;
	wait_queue_head_t request_wq;
} misc_sample_dev;

// 初始化标志
int _dev_init_status = 0;

/*
* 功能： 读取设备中所有的请求消息
* 入参total：读取的消息累计个数
* 入参kbuf: 读取的消息缓存
* 入参size: 需要读取的数据大小
* 反参: 实际读取的数据大小
*/
static int read_requests_locked(int *total, char *kbuf, size_t size)
{
	misc_sample_request *req;
	char *p = kbuf;
	mutex_lock(&misc_sample_dev.lock);
	list_for_each_entry(req, &misc_sample_dev.head, entry) {
		printk("read_requests_locked, req->result: %d, req->message: %s", req->result, req->message);
		if (req->result != MISC_SAMPLE_RET_UNKNOWN)
			continue;

		*total = (*total) + 1;
		if (strlen(req->message) + 1 + p > kbuf + size)
			break;
		strcpy(p, req->message); // char *strcpy(char *dest, const char *src)
		// req->result = MISC_SAMPLE_RET_WAIT;
		p += strlen(p) + 1;
	}
	mutex_unlock(&misc_sample_dev.lock);
	return p - kbuf;
}

static misc_sample_request* create_misc_sample_request(const int msg_type, const char* target)
{
	int path_size = 0;
	char uuid[MAX_UUID_LEN] = {0};
	misc_sample_request *req = NULL;

	memset(uuid, 0, MAX_UUID_LEN);
	snprintf(uuid, MAX_UUID_LEN, "%d-%u-", msg_type, get_random_u32());
	path_size = strlen(uuid);
	if (target) {
		path_size += strlen(target);
	}
	req = kzalloc(sizeof(misc_sample_request) + path_size + 1, GFP_KERNEL);

	if (unlikely(req == 0))
		return 0;

	req->pid = current->pid;
	strcpy(req->message, uuid);
	if (target){
	       strcat(req->message, target);
	}
	req->result = MISC_SAMPLE_RET_UNKNOWN;

	mutex_lock(&misc_sample_dev.lock);
	list_add_tail(&req->entry, &misc_sample_dev.head);
	mutex_unlock(&misc_sample_dev.lock);
	return req;
}

static ssize_t misc_sample_dev_read(struct file *filep, char __user *buf, size_t size, loff_t *offset)
{
    DECLARE_WAITQUEUE(wait, current);
    char *kbuf = NULL;
    int real_read_size = 0; // 实际读取的数据大小
    int msg_read_count = 0; // 读取的消息个数

    if (1 >= size) {
        return -EINVAL;
    }

    kbuf = kzalloc(size, GFP_KERNEL);
    if (unlikely(kbuf == 0)) {
        return -ENOSPC;
    }

    add_wait_queue(&misc_sample_dev.request_wq, &wait);

    for (;;) {
		msg_read_count = 0;
		real_read_size = read_requests_locked(&msg_read_count, kbuf, size);
		// printk("read_requests_locked, real_read_size: %d", real_read_size);
		// 读取到数据，则返回结果
		if (real_read_size) {
			break;
		}

		// 如果没有数据
		if (0 == msg_read_count) {
			if (filep->f_flags & O_NONBLOCK) {
				real_read_size = -EAGAIN;
				goto readout;
			}

			// 设置当前进程可为可中断状态
			__set_current_state(TASK_INTERRUPTIBLE);
			//TODO: better to move unlock here (quite some code needs modification then)
			schedule(); // ?相当于处理循环，先处理一遍其他任务
		} else if (0 == real_read_size) {
			goto readout;
		}

		if (signal_pending(current)) {
			real_read_size = -EINTR;
			goto readout;
		}
    }

	// 复制到用户空间
	if (copy_to_user(buf, kbuf, real_read_size)) {
		real_read_size = - EINTR;
	}

readout:
	// debug
	printk("misc sample read: %s", kbuf);
	remove_wait_queue(&misc_sample_dev.request_wq, &wait);
	set_current_state(TASK_RUNNING);
	kfree(kbuf);
	return real_read_size;
}

static ssize_t misc_sample_dev_write(struct file *filep, const char __user *buf, size_t size, loff_t *offset)
{
	printk("misc sample start writing, size: %lu", size);
	misc_sample_request *req = NULL;
	char *kbuf = NULL;
	char *kbuf_index = NULL; // 缓存检索指针
	int response_cnt = 0; // 响应消息计数

	if (sizeof(unsigned char) + 1 >= size) {
		return -EINVAL;
	}

	kbuf = kzalloc(size, GFP_KERNEL);
	if (unlikely(kbuf == 0)) {
		return -ENOSPC;
	}

	if (copy_from_user(kbuf, buf, size)) {
		kfree(kbuf);
		return -EFAULT;
	}

	// debug 转成结构体
	struct net_filter_change_policy change_policy = *(struct net_filter_change_policy *)kbuf;
	printk("%d",change_policy.policy.dst_addr_begin.ip);

	// 末尾没有结束符，则表示异常
	if (0 != kbuf[size - 1]) {
		kfree(kbuf);
		return -EINVAL;
	}

	kbuf_index = kbuf;
	mutex_lock(&misc_sample_dev.lock);
	while (kbuf + size > kbuf_index) {
		unsigned char result = *(unsigned char*)kbuf_index;
		kbuf_index++;
		
		printk("==%d\n", result);
		// 排除异常情况
		if (MISC_SAMPLE_RET_OK > result || MISC_SAMPLE_RET_UNKNOWN <= result) {
			continue;
		}


		list_for_each_entry(req, &misc_sample_dev.head, entry) {
			// 找到相应的消息，并设置结果
			if (strcmp(req->message, kbuf_index) /*|| MISC_SAMPLE_RET_WAIT < req->result*/) {
				continue;
			}

			req->result = result;
			response_cnt++;
		}
		// 切到下一个字符串首
		kbuf_index += strlen(kbuf_index) + 1;
	}
	mutex_unlock(&misc_sample_dev.lock);

	// debug
	printk("misc sample write: %s\n", kbuf);
	kfree(kbuf);
	if (response_cnt) {
		wake_up_interruptible(&misc_sample_dev.response_wq);
	}

	return size;
}

static struct file_operations misc_sample_dev_fops = {
	.owner = THIS_MODULE,
	.read = misc_sample_dev_read,
	.write = misc_sample_dev_write,
};

int register_misc_sample_dev(void)
{
	int error = 0;
	if (0 != _dev_init_status) {
		return 0;
	}
	_dev_init_status = 1;

	misc_sample_dev.misc.minor = MISC_DYNAMIC_MINOR;
	misc_sample_dev.misc.name = "misc_sample";
	misc_sample_dev.misc.fops = &misc_sample_dev_fops;
	misc_sample_dev.controller = 0;
	mutex_init(&misc_sample_dev.lock);
	INIT_LIST_HEAD(&misc_sample_dev.head);
	init_waitqueue_head(&misc_sample_dev.response_wq);
	init_waitqueue_head(&misc_sample_dev.request_wq);
	error = misc_register(&misc_sample_dev.misc);
	if (!error) {
		_dev_init_status = 2;
	} else {
		_dev_init_status = 0;
	}

	return error;
}

void unregister_misc_sample_dev(void)
{
	misc_deregister(&misc_sample_dev.misc);
}

int __init init_module(void)
{
	int error = 0;
    error = register_misc_sample_dev();
	create_misc_sample_request(1, "1");
	create_misc_sample_request(1, "2");
	create_misc_sample_request(1, "3");
    pr_info("misc sample dev module init finished");

	return error;
}

void __exit cleanup_module(void)
{
    unregister_misc_sample_dev();
    pr_info("misc sample dev module exit");
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("caitao");
MODULE_DESCRIPTION("misc sample dev module");